package rocks.susurrus.susurrus.utils;
import android.util.Base64;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SealedObject;
import javax.crypto.SecretKey;
import rocks.susurrus.susurrus.models.EncryptionModel;
/**
* Created by simon on 14.06.15.
*/
public class Crypto {
private static final String CRYPTO_ALGO = "RSA";
/**
* Generates a private- and public-RSA-Key with 2048 bits.
*
* @return Arraylist with privateKey [0] and publicKey [1].
* @throws RuntimeException
*/
public static ArrayList generateKeys() throws RuntimeException {
ArrayList keys = new ArrayList();
// initiate the key-generator (rsa)
try {
KeyPairGenerator generator = KeyPairGenerator.getInstance(CRYPTO_ALGO);
SecureRandom randomNumbers = new SecureRandom();
generator.initialize(2048, randomNumbers);
// generate key-pair
KeyPair pair = generator.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
// preparing keys
keys.add(privateKey);
keys.add(publicKey);
} catch(NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("Keys could not be generated");
}
return keys;
}
/**
* Encodes a given public-/private-Key as base64 string.
* @param key
* @return
*/
public static String keyToString(Key key) {
byte[] keyBytes = key.getEncoded();
String key64 = Base64.encodeToString(keyBytes, Base64.DEFAULT);
return key64;
}
/**
* Converts a given base64-String into an usable publicKey.
*
* @param keyString Base64-String of a publicKey.
* @return PublicKey.
*/
public static PublicKey publicStringToKey(String keyString) {
// convert back to bytes and construct an X509EncodedKeySpec from it
byte[] keyBytes = Base64.decode(keyString, Base64.DEFAULT);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
// obtain the publicKey
PublicKey publicKey = null;
try {
KeyFactory generator = KeyFactory.getInstance(CRYPTO_ALGO);
publicKey = generator.generatePublic(keySpec);
} catch(NoSuchAlgorithmException e) {
e.printStackTrace();
} catch(InvalidKeySpecException e) {
e.printStackTrace();
}
return publicKey;
}
/**
* Converts a given base64-String into an usable privateKey.
*
* @param keyString
* @return
*/
public static PrivateKey privateStringToKey(String keyString) {
// convert back to bytes and construct an PKCS8EncodedKeySpec from it
byte[] keyBytes = Base64.decode(keyString, Base64.DEFAULT);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
// obtain the publicKey
PrivateKey privateKey = null;
try {
KeyFactory generator = KeyFactory.getInstance(CRYPTO_ALGO);
privateKey = generator.generatePrivate(keySpec);
} catch(NoSuchAlgorithmException e) {
e.printStackTrace();
} catch(InvalidKeySpecException e) {
e.printStackTrace();
}
return privateKey;
}
/**
* Encrypts an unencrypted serializable Object with AES.
*
* At first an AES-Session-Key is generated for encrypting the handed SealedObject. The AES-
* Session-Key is wrapped with the user's publicKey. The wrapped-key can be used to decrypt
* the SealedObject later.
*
* @param _unencryptedObj Serializable object.
* @param _publicKey The user's publicKey.
* @return An EncryptionModel with SealedObject and wrapped-Key.
*/
public static EncryptionModel encryptBytes(Serializable _unencryptedObj,
PublicKey _publicKey) {
// generate a random AES-key for encrypting the object's data
SecretKey aesSessionKey;
try {
// generate key
KeyGenerator generator = KeyGenerator.getInstance("AES");
SecureRandom randomNumbers = new SecureRandom();
generator.init(256, randomNumbers);
aesSessionKey = generator.generateKey();
} catch(NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
// initiate the encryption cipher
Cipher cipher;
byte[] wrappedKey;
try {
// get a cipher object that implements the RSA transformation
cipher = Cipher.getInstance(CRYPTO_ALGO);
// initialize cipher with the public key. Use WRAP_MODE for wrapping the encrypted
// AES-data with the handed public key.
cipher.init(Cipher.WRAP_MODE, _publicKey);
// wrap AES-key
wrappedKey = cipher.wrap(aesSessionKey);
// get a new cipher and use the wrapped AES-key for encryption
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, aesSessionKey);
} catch(NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch(NoSuchPaddingException e) {
e.printStackTrace();
return null;
} catch(InvalidKeyException e) {
e.printStackTrace();
return null;
} catch(IllegalBlockSizeException e) {
e.printStackTrace();
return null;
}
// use the cipher for encryption
SealedObject encryptedObj;
try {
// creates a new SealedObject instance wrapping the specified object and sealing it
// using the specified cipher.
encryptedObj = new SealedObject(_unencryptedObj, cipher);
} catch(IllegalBlockSizeException e) {
e.printStackTrace();
return null;
} catch(NullPointerException e) {
e.printStackTrace();
return null;
} catch(IOException e) {
e.printStackTrace();
return null;
}
return new EncryptionModel(encryptedObj, wrappedKey);
}
/**
* Decrypts an AES encrypted serializable Object with the handed wrapped- and private-Key.
*
* At first the AES-Session-Key is restored from the user's privateKey which unwraps it from
* the wrappedKey. The restored AES-Session-Key is used to decrypt the handed SealedObject.
*
* @param _encryptedObj RSA-encrypted sealedObject.
* @param _privateKey The user's privateKey.
* @return An unencrypted Object.
*/
public static Object decryptBytes(SealedObject _encryptedObj, PrivateKey _privateKey,
byte[] _wrappedKey) {
// initiate the decryption cipher
Cipher cipher;
try {
// get a Cipher object that implements the RSA transformation
cipher = Cipher.getInstance(CRYPTO_ALGO);
// initialize this cipher with the private key from the given certificate and use it
// for decryption
cipher.init(Cipher.UNWRAP_MODE, _privateKey);
Key aesSessionKey = cipher.unwrap(_wrappedKey, "AES", Cipher.SECRET_KEY);
cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, aesSessionKey);
} catch(NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch(NoSuchPaddingException e) {
e.printStackTrace();
return null;
} catch(InvalidKeyException e) {
e.printStackTrace();
return null;
}
// use the cipher for decryption
Object decryptedObj;
try {
// returns the wrapped object, decrypting it using the specified key
decryptedObj = _encryptedObj.getObject(cipher);
} catch(IOException e) {
e.printStackTrace();
return null;
} catch(ClassNotFoundException e) {
e.printStackTrace();
return null;
} catch(IllegalBlockSizeException e) {
e.printStackTrace();
return null;
} catch(BadPaddingException e) {
e.printStackTrace();
return null;
}
return decryptedObj;
}
}